// C source file with the function definitions to handle files

// wrappers to some stdio.h functions

// def_fopen() function
// wrapper for fopen() to check errors
FILE * def_fopen(char * fpath, char * open_mode)
{
  // create fp
  FILE * fp = fopen(fpath, open_mode);
  
  // check if the file could be opened
  if (fp == NULL) {
    printf("\nFailed to open %s\n", fpath);
    perror("Error");
    printf("\n");
    exit(1);
  }
  // return fp
  return fp;
}

// def_fgetc() function
// wrapper for fgetc()
smax def_fgetc(FILE * fp)
{
  // this wrapper exists in case
  // I need it for something
  return (smax) fgetc(fp);
}

// def_fseek() function
// wrapper for fseek() to check errors
void def_fseek(FILE * fp, smax offset, smax whence)
{
  if (fseek(fp, offset, whence) != 0)
  {
    printf("\nFailed seeking in a file.\n");
    printf("Terminating program.\n");
    exit(1);
  }
}

// def_fputc() function
// wrapper for fputc() to check errors
void def_fputc(smax ch, FILE * fp)
{
  if (fputc(ch, fp) == EOF)
  {
    printf("\nFailed to write data on file.\n");
    perror("Error");
    printf("\n");
    exit(1);
  }
}

// def_fclose() function
// wrapper for fclose() to check errors
void def_fclose(FILE * fp)
{
  if (fclose(fp) == EOF)
  {
    printf("\nFailed to close a file.\n");
    printf("Terminating program.\n");
    exit(1);
  }
}

// path string related functions

// get_fpath() function
// get the path of a file into another string array the function will
// return a pointer to the last element written (NULL character)
char * get_file_dir(char * fpath, char * out_file_dir)
{
  // check params
  if (fpath == NULL || out_file_dir == NULL)
    goto err;
  
  // get the string size
  umax str_size = get_str_byte_length(fpath, FILE_PATH_ENC);
  if (str_size == 0)
    goto err;
  
  // search for the first indicator of a path
  for (umax i = 0; i < str_size; i++)
    // first slash found when reading from right to left
    if (fpath[str_size - i] == '/' || fpath[str_size - i] == '\\')
      // copy the path into out_file_dir and return a pointer to the last element
      return cp_mem_bytes(fpath, str_size - i, out_file_dir);
  
  // no slash found? that means it is
  // in the same directory (probably)
  return out_file_dir;
  // failure
  err:
  return NULL;
}

// get_file_ext() function
// this function will get the file extension of a file path string
// (a pointer to the start of the extension, without the dot).
// if no extension is found the function will return the 
// pointer to the terminating null character
char * get_file_ext(char * fpath)
{
  // check params
  if (fpath == NULL)
    goto err;
  
  // get string length
  umax str_size = get_str_byte_length(fpath, FILE_PATH_ENC);
  if (str_size == 0)
    goto err;
  
  // read string from right to left to search for the first dot  
  for (umax i = 0; i < str_size; i++) {
    // dot found
    if (fpath[str_size - i] == '.')
      return fpath + str_size - i + 1;
    // if a slash is found before a dot then the file has no extension
    else if (fpath[str_size - i] == '/' || fpath[str_size - i] == '\\')
      goto end;
  }
  // if no slash is found and no point is found, 
  // still, return a pointer to the null character
  end:
  return fpath + str_size - 1;
  
  // failure
  err:
  return NULL;
}

// get_file_size() function
// function that gets the size in bytes of a file
umax get_file_size(char * fpath)
{  
  // check params
  if (fpath == NULL)
    return 0;
  
  // open file
  FILE * fp = def_fopen(fpath, "rb");
  // read it completely
  umax size = 0;
  while (def_fgetc(fp) != EOF)
    size++;
  
  // close file
  def_fclose(fp);  
  return size;
}

// get_fdata_array() function
// get bytes from a file and store them on a manually allocated memory array
void * get_fbytes(char * fpath, umax pos, umax size)
{
  // check params
  byte * fbytes = NULL, * ptr = NULL;
  if (fpath == NULL || size == 0)
    goto end;
  
  // load the file and go to array_pos
  fbytes = load_file_mem(fpath);
  umax fsize = get_file_size(fpath);
  if (fbytes == NULL)
    goto end;
  
  // nono, you can't just do that
  if (fsize < pos + size)
    goto end;
  
  // copy desired bytes into the array to return
  ptr = allocate_memory(size, 1); // variable to return
  if (ptr == NULL)
    goto end;
  cp_mem_bytes(fbytes + pos, size, ptr);
  
  // done!
  end:
  free_memory(fbytes);
  return ptr;
}

// load_file_mem() function
// function that opens a file and loads it on memory
void * load_file_mem(char * fpath)
{
  // check params
  if (fpath == NULL)
    goto err;
  
  // open file
  FILE * fp = def_fopen(fpath, "rb"); 
  // create space for file in memory (remember to free it!)
  // size is larger by 1 since it helps for reading
  byte * fdata = allocate_memory(get_file_size(fpath), 1);
  if (fdata == NULL) {
    def_fclose(fp);
    goto err;
  }
  
  // load it on memory
  smax ch = 0;
  for (umax i = 0; (ch = def_fgetc(fp)) != EOF; i++)
    fdata[i] = ch;  
  // close file
  def_fclose(fp);
  
  // return pointer
  return fdata; 
  // failure
  err:
  return NULL;
}

// dump_mem_file() function
// function to dump the contents of memory (bytes) into a file
umax dump_mem_file(char * fpath, umax file_size, void * fdata)
{
  // check params
  if (fpath == NULL || file_size == 0 || fdata == NULL)
    return 0;
  
  // create file
  FILE * fp = def_fopen(fpath, "wb");  
  // cast fdata pointer
  byte * data = fdata;
  
  // dump everything from 
  // data into the new file
  for (umax i = 0; i < file_size; i++)
    def_fputc(data[i], fp);    
  // close the file and indicate
  // that the file was created
  def_fclose(fp);
  printf("File %s was created.\n", fpath);
  
  // done!
  return file_size;
}
